#ifndef DATALINE_H_INCLUDED
#define DATALINE_H_INCLUDED

#include <sstream>
#include <string>
#include <map>
#include <vector>
#include <limits>
#include <ctime>
#include "stringtonumb.h"


namespace STLData{

const int OutOfRange = 10;

class Date {
    public:
        time_t epoch;
        struct tm date;

    public:
        Date() : epoch(0) { date = *(localtime(&epoch)) ;}
        Date(time_t ValueEpoch) : epoch(ValueEpoch) { date = *(localtime(&epoch)) ;}
        Date(struct tm * valueDate) {
            date = *valueDate;
            epoch = mktime(valueDate);
        }

        template<class T>
        Date & operator=(const T value) {
            epoch = (time_t) value;
            date = *(localtime(&epoch));
            return *this;
        }

        operator time_t() const {
            return epoch;
        }
        operator struct tm() const {
            return date;
        }
        operator struct tm*() const {
            struct tm * pt;
            pt = (struct tm *)malloc(sizeof(date));
            *pt = date;
            return pt;
        }
        operator double() const {
            return (double) epoch;
        }
        operator float() const {
            return (float) epoch;
        }
        operator int() const {
            return (int) epoch;
        }
        operator std::string() const {
            std::string outstr;
            outstr.assign(asctime(&date));
            return outstr;
        }
        operator char *() const {
            return asctime(&date);
        }
        operator unsigned int() const {
            return (unsigned int) epoch;
        }

}

class dataLineElement {
    friend class dataLine;
    private:
        typedef struct {
            std::string fieldKey;
            long valueInt;
            double valueReal;
            std::string valueText;
            Date valueDate;
            char fieldType;
            int stringLength;
        } fieldValue;
        fieldValue fieldData;
    public:
        dataLineElement& operator=(const dataLineElement &rhs){
            if (this == &rhs)
                return *this;
            switch (rhs.fieldData.fieldType) {
                case 'T':
                    *this = rhs.fieldData.valueText;
                    break;
                case 'R':
                    *this = rhs.fieldData.valueReal;
                    break;
                case 'I':
                    *this = rhs.fieldData.valueInt;
                    break;
                case 'D':
                    *this = rhs.fieldData.valueDate;
                    break;
                default:
                    fieldData.valueText = "NaN";
                    fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                    fieldData.valueInt  = std::numeric_limits<long>::min();
                    fieldData.valueDate  = std::numeric_limits<long>::min();
                    break;
            }
            return *this;

        }

        dataLineElement& operator=(const std::string valueText) {

            std::ostringstream convert;

            switch (fieldData.fieldType) {
                case 'T':
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = valueText.size();
                    fieldData.valueText = valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = std::numeric_limits<long>::min();
                    fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                    break;
                case 'R':
                    fieldData.valueReal = StringToNumber<double>(valueText);
                    convert << fieldData.valueReal;
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = (long) fieldData.valueReal;
                    break;
                case 'I':
                    fieldData.valueInt = StringToNumber<long>(valueText);
                    convert << fieldData.valueInt;
                    fieldData.valueText = convert.str();
                    fieldData.valueReal = (double) fieldData.valueInt;
                    break;
                case 'D':
                    fieldData.valueDate = StringToNumber<time_t>(valueText);
                    fieldData.valueText = (std::string)fieldData.valueDate;
                    fieldData.valueReal = (double) fieldData.valueDate;
                    fieldData.valueInt = (long) fieldData.valueDate;
                    break;
                default:
                    fieldData.fieldType = 'T';
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = valueText.size();
                    fieldData.valueText = valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = std::numeric_limits<long>::min();
                    fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                    fieldData.valueDate = std::numeric_limits<time_t>::min();
                    break;

            }
            return *this;
        }


        dataLineElement& operator=(const double valueReal){
            std::ostringstream convert;

            switch (fieldData.fieldType) {
                case 'T':
                    convert << valueReal;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = convert.str().size();
                    fieldData.valueText = convert.str().substr(0, fieldData.stringLength);
                    fieldData.valueInt = std::numeric_limits<long>::min();
                    fieldData.valueDate = std::numeric_limits<time_t>::min();
                    fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                    break;
                case 'R':
                    convert << valueReal;
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = (long) valueReal;
                    fieldData.valueReal = valueReal;
                    fieldData.valueDate = valueReal;
                    break;
                case 'I':
                    fieldData.valueInt = (long) valueReal;
                    convert << fieldData.valueInt;
                    fieldData.valueText = convert.str();
                    fieldData.valueReal = (double) fieldData.valueInt;
                    fieldData.valueDate = fieldData.valueInt;
                    break;
                case 'D':
                    fieldData.valueDate = valueReal;
                    date = localtime(&fieldData.valueDate);
                    fieldData.valueText = (std::string) fieldData.valueDate;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = fieldData.valueText.size();
                    fieldData.valueText = fieldData.valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = (long) fieldData.valueDate;
                    fieldData.valueReal = (double) fieldData.valueDate;
                    break;
                default:
                    fieldData.fieldType = 'R';
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = (long) valueReal;
                    fieldData.valueReal = valueReal;
                    fieldData.valueDate = valueReal;
                    break;
            }
            return *this;
        }


        dataLineElement& operator=(const long valueInt){
            std::ostringstream convert;

            switch (fieldData.fieldType) {
                case 'T':
                    convert << valueInt;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = convert.str().size();
                    fieldData.valueText = convert.str().substr(0, fieldData.stringLength);
                    fieldData.valueInt = std::numeric_limits<long>::min();
                    fieldData.valueDate = std::numeric_limits<time_t>::min();
                    fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                    break;
                case 'R':
                    fieldData.valueReal = (double) valueInt;
                    convert << fieldData.valueReal;
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = (long) fieldData.valueReal;
                    fieldData.valueDate = fieldData.valueReal;
                    break;
                case 'I':
                    convert << valueInt;
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = valueInt;
                    fieldData.valueDate = valueInt;
                    fieldData.valueReal = (double) valueInt;
                    break;
                case 'D':
                    fieldData.valueDate = valueInt;
                    fieldData.valueText = (std::string) fieldData.valueDate;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = fieldData.valueText.size();
                    fieldData.valueText = fieldData.valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = (long) fieldData.valueDate;
                    fieldData.valueReal = (double) fieldData.valueDate;
                    break;
                default:
                    fieldData.fieldType = 'I';
                    convert << valueInt;
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = valueInt;
                    fieldData.valueDate = valueInt;
                    fieldData.valueReal = (double) valueInt;
                    break;
            }
            return *this;
        }

        dataLineElement& operator=(const Date valueDate){
            std::ostringstream convert;

            switch (fieldData.fieldType) {
                case 'T':
                    fieldData.valueText = (std::string) valueDate;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = fieldData.valueText.size();
                    fieldData.valueText = fieldData.valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = std::numeric_limits<long>::min();
                    fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                    fieldData.valueDate = std::numeric_limits<time_t>::min();
                    break;
                case 'R':
                    fieldData.valueReal = (double) valueDate;
                    convert << fieldData.valueReal;
                    fieldData.valueText = convert.str();
                    fieldData.valueInt = (long) fieldData.valueReal;
                    fieldData.valueDate = fieldData.valueReal;
                    break;
                case 'I':
                    fieldData.valueInt = (long) valueDate;
                    convert << fieldData.valueInt;
                    fieldData.valueText = convert.str();
                    fieldData.valueReal = (double) fieldData.valueInt;
                    fieldData.valueDate = fieldData.valueInt;
                    break;
                case 'D':
                    fieldData.valueText = (std::string) valueDate;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = fieldData.valueText.size();
                    fieldData.valueText = fieldData.valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = (long) valueDate;
                    fieldData.valueReal = (double) valueDate;
                    fieldData.valueDate = valueDate;
                    break;
                default:
                    fieldData.fieldType = 'D';
                    fieldData.valueText = (std::string) valueDate;
                    if (fieldData.stringLength == 0 )
                        fieldData.stringLength = fieldData.valueText.size();
                    fieldData.valueText = fieldData.valueText.substr(0, fieldData.stringLength);
                    fieldData.valueInt = (long) valueDate;
                    fieldData.valueReal = (double) valueDate;
                    fieldData.valueDate = valueDate;
                    break;
            }
            return *this;
        }

        dataLineElement& operator=(const char * valueText) {
            std::string valueString;
            if (fieldData.stringLength!=0) {
                valueString.assign(valueText, fieldData.stringLength);
            } else {
                valueString.assign(valueText);
            }
            return operator=(valueString);
        }

        dataLineElement& operator=(const char valueChar) {
            std::string valueString;
            valueString = valueChar;
            return operator=(valueString);
        }

        dataLineElement& operator=(const int valueInt) {
            long valueLong;
            valueLong = (long) valueInt;
            return operator=(valueLong);
        }

        dataLineElement& operator=(const float valueFloat) {
            double valueDouble;
            valueDouble = (double) valueFloat;
            return operator=(valueDouble);
        }

        dataLineElement& operator=(const struct tm * valueDate) {
            Date date2;
            date2 = *valueDate;
            return operator=(date2);
        }

        operator double() const {
            return fieldData.valueReal;
        }

        operator float() const {
            return (float)fieldData.valueReal;
        }

        operator long() const {
            return fieldData.valueInt;
        }

        operator Date() const {
            return fieldData.valueDate;
        }

        operator int() const {
            return (int)fieldData.valueInt;
        }

        operator std::string() const {
            return fieldData.valueText;
        }

        operator struct tm *() const {
            return (struct tm *)fieldData.valueDate;
        }

        bool operator==(const dataLineElement &other) const;
        bool operator!=(const dataLineElement &other) const;
        bool operator<=(const dataLineElement &other) const;
        bool operator>=(const dataLineElement &other) const;
        bool operator<(const dataLineElement &other) const;
        bool operator>(const dataLineElement &other) const;

 /*       operator char*() const {
            return (char *)fieldData.valueText.c_str();
        }
*/
        std::string stringValue() {
            return fieldData.valueText;
        }
        int length() {
            return fieldData.stringLength;
        }
        double doubleValue() {
            return fieldData.valueReal;
        }
        long longValue() {
            return fieldData.valueInt;
        }
        char type() {return fieldData.fieldType;}
        const std::string key() const {return fieldData.fieldKey;}


};


class dataLine {
    friend class list;
    private:
        typedef dataLineElement fieldValue;
        typedef std::string keyType;
        std::map<keyType, int> hashKey;
        typedef std::vector<dataLineElement> lineStruct;
        lineStruct lineValues;
        int maxIndex;

    public:
        typedef lineStruct::iterator iterator;
        typedef lineStruct::const_iterator const_iterator;

    private:
        int getIndex(keyType searchKey) {
            if (hashKey.find(searchKey)!= hashKey.end() ) {
                return hashKey[searchKey] ;
            } else {
                return -1;
            }
        }
        int getIndex(const keyType searchKey) const {
            if (hashKey.find(searchKey)!= hashKey.end() ) {
                return hashKey.find(searchKey)->second;
            } else {
                return -1;
            }
        }

    public:
        iterator begin() { return lineValues.begin(); }
        const_iterator begin() const { return lineValues.begin(); }

        iterator end() { return lineValues.end(); }
        const_iterator end() const { return lineValues.end(); }

        iterator find(const keyType searchKey) {
            int index;
            index = getIndex(searchKey);
            if (index == -1) {
                return end();
            }
            return (lineValues.begin() + index);
        }
        const_iterator find(const keyType searchKey) const {
            int index;
            index = getIndex(searchKey);
            if (index == -1) {
                return end();
            }
            return (lineValues.begin() + index);
        }


        dataLineElement& operator[](const keyType searchKey) {
            int index;
            index = getIndex(searchKey);
            if (index == -1) {
                hashKey[searchKey] = maxIndex;
                index = maxIndex;
                ++maxIndex;
                lineValues[index].fieldData.fieldKey = searchKey;
                lineValues[index].fieldData.fieldType = 0;
                lineValues[index].fieldData.stringLength = 0;
                lineValues[index].fieldData.valueText = "NaN";
                lineValues[index].fieldData.valueReal = std::numeric_limits<double>::quiet_NaN();
                lineValues[index].fieldData.valueInt = std::numeric_limits<long>::min();
            }
            return lineValues[index];
        }

        dataLineElement const& operator[](const keyType searchKey) const {
            int index;
            std::string search = searchKey;
            index = this->getIndex(search);
            if (index == -1) {
                throw OutOfRange;
            }
            return lineValues[index];
        }

        dataLine& operator=(const dataLine &rhs);
        dataLine& corresponding(const dataLine &rhs);

        bool insert(keyType searchKey, long value);
        bool insert(keyType searchKey, double value);
        bool insert(keyType searchKey, std::string value);
        bool createField(keyType searchKey, char fieldType, int length = 0);

};

}

#endif // DATALINE_H_INCLUDED
